package org.hertsig.magiccardmarket;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.io.IOException;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.hertsig.magiccardmarket.entities.Articles;
import org.hertsig.magiccardmarket.entities.Articles.Article;
import org.hertsig.magiccardmarket.entities.out.AddedArticle;
import org.hertsig.magiccardmarket.entities.out.ChangeArticles;
import org.hertsig.magiccardmarket.entities.out.ChangedArticle;
import org.hertsig.magiccardmarket.xml.XmlMarshaller;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

@Slf4j
public class StockApi extends ApiBase {
	public StockApi(MagicCardMarketApi api) {
		super(api);
	}
	
	public Iterable<Article> getStock() {
		return new PartialContentIterable<Article>(Articles.class, (nextStartIndex) -> api.execute(new HttpGet(api.stock(nextStartIndex))));
	}
	
	public Article getArticle(String id) {
		return Iterables.getOnlyElement(XmlMarshaller.unmarshal(api.executeAndGet(new HttpGet(api.stock(id))), Articles.class));
	}
	
	/**
	 * Add an article to the authenticated user's stock.
	 * The mandatory fields are product id, price, amount and language.
	 * 
	 * Note that the API docs state that the 5 boolean flags default to false if not specified,
	 * but at the time of writing they actually default to true
	 * 
	 * @return the {@link Article} that was added
	 * @throws NullPointerException if the mandatory fields are not filled
	 * @throws IllegalArgumentException if the mandatory fields are filled but not valid
	 */
	@SneakyThrows(IOException.class)
	public Article addArticle(ChangeArticles.ChangeArticle article) {
		checkNotNull(article.getIdProduct(), "idProduct");
		checkArgument(checkNotNull(article.getPrice(), "Price") > 0d, "Price must be > 0.00");
		checkArgument(checkNotNull(article.getAmount(), "Amount") > 0, "Amount must be > 0");
		checkArgument(checkNotNull(article.getLanguage(), "Language") > 0, "Invalid language");
		
		ChangeArticles toAdd = new ChangeArticles(Lists.newArrayList(article));
		HttpPost post = new HttpPost(api.stock());
		HttpResponse response = setEntityAndExecute(post, toAdd);
		
		AddedArticle added = XmlMarshaller.unmarshal(response.getEntity().getContent(), AddedArticle.class);
		if (added.getSuccess() == null || !added.getSuccess()) {
			log.warn("Add failed with message {}: {}", added.getMessage(), added.getTried());
			return null;
		}
		return added.getIdArticle();
	}
	
	/**
	 * Change an article in the authenticated user's stock
	 * Only the product id must be supplied. That product is updated with all the other values specified.
	 * 
	 * Note that the Amount field specifies the amount of articles to update, not the new amount of cards for this article.
	 * 
	 * @return the {@link Article} that was changed
	 */
	@SneakyThrows(IOException.class)
	public Article changeArticle(ChangeArticles.ChangeArticle article) {
		checkNotNull(article.getIdArticle(), "idArticle");
		
		ChangeArticles toChange = new ChangeArticles(Lists.newArrayList(article));
		HttpPut put = new HttpPut(api.stock());
		HttpResponse response = setEntityAndExecute(put, toChange);
		
		ChangedArticle changed = XmlMarshaller.unmarshal(response.getEntity().getContent(), ChangedArticle.class);
		if (changed.getSuccess() == null || !changed.getSuccess()) {
			log.warn("Update failed with message {}: {}", changed.getMessage(), changed.getTried());
			return null;
		}
		return changed.getIdArticle();
	}
	
	/**
	 * Delete an article from the authenticated user's stock
	 * Only the article id and amount fields are used and mandatory
	 * @return the amount of successfully deleted articles
	 * @throws UnsupportedOperationException always. MKM expects a body with this DELETE request, which violates the spec and isn't supported by Apache HTTP client.
	 */
	public int deleteArticle(ChangeArticles.ChangeArticle article) {
		checkNotNull(article.getIdArticle(), "idArticle");
		checkNotNull(article.getAmount(), "amount");
		checkArgument(article.getAmount() > 0 && article.getAmount() <= 32, "Amount must be between 1 and 31");
		throw new UnsupportedOperationException("MKM expects a body with this DELETE request, which violates the spec and isn't supported by Apache HTTP client");
		
//		ChangeArticles toDelete = new ChangeArticles(Lists.newArrayList(article));
//		HttpDelete delete = new HttpDelete(api.stock());
//		HttpResponse response = setEntityAndExecute(delete, toDelete);
//		
//		// the API returns <success>some number</success> if it succeeds, and <success>false</success> if it doesn't. JAXB cannot parse that.
//		DeletedArticle deleted = XmlMarshaller.unmarshal(response.getEntity().getContent(), DeletedArticle.class);
//		return deleted.getSuccess() == null ? 0 : deleted.getSuccess();
	}
	
	@SneakyThrows(IOException.class)
	public Iterable<Article> increaseArticleCount(String articleId, int amount) {
		HttpResponse response = api.execute(new HttpPut(api.stockIncrease(articleId, amount)));
		log.debug("PUT completed with response code {}", response.getStatusLine().getStatusCode());
		return XmlMarshaller.unmarshal(response.getEntity().getContent(), Articles.class);
	}
	
	@SneakyThrows(IOException.class)
	public Iterable<Article> decreaseArticleCount(String articleId, int amount) {
		HttpResponse response = api.execute(new HttpPut(api.stockDecrease(articleId, amount)));
		log.debug("PUT completed with response code {}", response.getStatusLine().getStatusCode());
		return XmlMarshaller.unmarshal(response.getEntity().getContent(), Articles.class);
	}
}
